package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.thread.UserContext;
import com.leyou.common.exceptions.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;

/**
 * (Order)表服务实现类
 *
 * @author makejava
 * @since 2020-08-17 14:53:56
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final ItemClient itemClient;
    private final UserClient userClient;
    private final OrderDetailService detailService;
    private final OrderLogisticsService logisticsService;
    private final PayHelper payHelper;
    private final AmqpTemplate amqpTemplate;

    public OrderServiceImpl(ItemClient itemClient, UserClient userClient, OrderDetailService detailService, OrderLogisticsService logisticsService, PayHelper payHelper, AmqpTemplate amqpTemplate) {
        this.itemClient = itemClient;
        this.userClient = userClient;
        this.detailService = detailService;
        this.logisticsService = logisticsService;
        this.payHelper = payHelper;
        this.amqpTemplate = amqpTemplate;
    }

    @Transactional
    @Override
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.Order
        Order order = new Order();
        // 1.1.订单id, 由MybatisPlus自带的雪花算法生成
        // 1.2.金额
        Map<Long, Integer> cartMap = orderFormDTO.getCarts();
        // 1.2.1.查询sku
        List<SkuDTO> skuList = itemClient.querySkuByIds(cartMap.keySet());
        // 1.2.2.计算总金额
        long total = 0;
        for (SkuDTO skuDTO : skuList) {
            Integer num = cartMap.get(skuDTO.getId());
            total += skuDTO.getPrice() * num;
        }
        // 1.2.3.赋值
        order.setTotalFee(total);
        // 1.2.4.邮费，需要其它微服务提供接口，目前全场包邮
        order.setPostFee(0L);
        // 1.2.5.实付金额: 总金额 + 邮费 - 优惠金额 = 实付金额
        order.setActualFee(order.getTotalFee() + order.getPostFee());
        // 1.2.5.支付类型
        order.setPaymentType(orderFormDTO.getPaymentType());

        // 1.3.用户id
        Long userId = UserContext.getUser().getId();
        order.setUserId(userId);

        // 1.4.订单状态
        order.setStatus(OrderStatus.INIT.getValue());

        // 1.5.写入数据库
        boolean success = save(order);
        if (!success) {
            throw new LyException(500, "新增订单失败");
        }

        // 获取订单id
        Long orderId = order.getOrderId();

        // 2.OrderDetail
        // 2.1.创建detail集合
        List<OrderDetail> details = new ArrayList<>(skuList.size());
        // 2.2.遍历sku，封装为OrderDetail
        for (SkuDTO skuDTO : skuList) {
            OrderDetail detail = convertSku2OrderDetail(cartMap, orderId, skuDTO);
            details.add(detail);
        }
        // 2.3.写入数据库
        success = detailService.saveBatch(details);
        if (!success) {
            throw new LyException(500, "新增订单失败");
        }

        // 3.OrderLogistics
        // 3.1.获取地址id
        Long addressId = orderFormDTO.getAddressId();
        // 3.2.根据地址id查询ly-user，得到用户地址
        AddressDTO address = userClient.queryAddressById(addressId);
        // 3.3.地址转换
        OrderLogistics logistics = address.toEntity(OrderLogistics.class);
        logistics.setOrderId(orderId);
        // 3.4.写入数据库
        success = logisticsService.save(logistics);
        if (!success) {
            throw new LyException(500, "新增订单失败");
        }

        // 4.减库存
        try {
            itemClient.deductStock(cartMap);
        } catch (FeignException e) {
            // 扣减库存失败
            throw new LyException(e.status(), e.contentUTF8());
        }

        // TODO 5.清理购物车

        // 6.发送MQ消息到延迟队列
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, orderId);
        return orderId;
    }

    @Override
    public String getPayUrl(Long orderId) {
        // 1.查询订单 Order
        Order order = getById(orderId);
        // 1.1.非空判断
        if (order == null) {
            throw new LyException(400, "订单id不存在！");
        }
        // 1.2.订单状态判断
        Integer status = order.getStatus();
        if (!Objects.equals(OrderStatus.INIT.getValue(), status)) {
            // 订单已经支付或关闭
            throw new LyException(400, "订单已经支付或关闭");
        }

        // 2.查询订单详情 OrderDetail
        List<OrderDetail> details = detailService.query().eq("order_id", orderId).list();
        String desc = details.get(0).getTitle();

        // 3.获取支付链接
        return payHelper.getPayUrl(orderId, desc, order.getActualFee());
    }

    @Override
    public void handleNotify(Map<String, String> data) {
        // 1.校验数据签名
        payHelper.checkReturnCode(data);
        payHelper.checkResultCode(data);
        payHelper.checkSignature(data);

        // 2.校验订单金额
        // 2.1.获取请求参数中的订单id和金额
        String totalFee = data.get("total_fee");
        String outTradeNo = data.get("out_trade_no");
        if(StringUtils.isBlank(totalFee) || StringUtils.isBlank(outTradeNo)){
            // 订单id和金额不存在，数据有误
            throw new LyException(400, "请求参数有误！");
        }
        // 2.2.查询数据库中的订单
        Long orderId = null;
        try {
            orderId = Long.valueOf(outTradeNo);
        } catch (NumberFormatException e) {
            throw new LyException(400, "订单id参数有误");
        }
        Order order = getById(orderId);
        if (order == null) {
            throw new LyException(400, "订单id参数有误");
        }
        // 2.3.比较金额
        if(!totalFee.equals(order.getActualFee().toString())){
            // 订单金额不一致
            throw new LyException(400, "订单金额有误，再捣乱报警了！");
        }

        // 3.幂等校验
        if (!Objects.equals(OrderStatus.INIT.getValue(), order.getStatus())) {
            // 订单已经处理过了，是重复通知
            return;
        }

        // 4.修改订单状态 update tb_order set status = 2 where order_id = 100
        update().set("status", OrderStatus.PAY_UP.getValue())
                .set("pay_time", new Date())
                .eq("order_id", orderId)
                .eq("status", OrderStatus.INIT.getValue())
                .update();
    }

    @Override
    public Integer queryOrderStatus(Long id) {
        // 查询订单
        Order order = getById(id);
        if (order == null) {
            throw new LyException(400, "订单id参数有误");
        }
        // 返回订单状态
        return order.getStatus();
    }

    @Override
    public void evictOrder(Long orderId) {
        // 1.查询
        Order order = getById(orderId);
        // 2.判断订单状态
        if (order == null) {
            // 订单id不存在，消息是错误的消息
            log.error("清理订单异常，订单id不存在：{}", orderId);
            return;
        }
        if(!order.getStatus().equals(OrderStatus.INIT.getValue())){
            // 订单已经支付或关闭，无需处理
            return;
        }
        // 3.如果未支付，清理订单
        // 3.1.关闭订单 update tb_order set status = 5, close_time = NOW() WHERE order_id = 1 AND status = 1
        update().set("status", OrderStatus.CLOSED.getValue()).set("close_time", new Date())
                .eq("order_id", orderId).eq("status", OrderStatus.INIT.getValue())
                .update();
        // 3.2.查询OrderDetail，
        List<OrderDetail> detailList = detailService.query().eq("order_id", orderId).list();
        if(CollectionUtils.isEmpty(detailList)){
            return;
        }
        // 3.3.获取商品及扣减的库存数量
        Map<Long, Integer> cartMap = detailList
                .stream().collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
        // 3.4.恢复库存
        itemClient.addStock(cartMap);
    }

    private OrderDetail convertSku2OrderDetail(Map<Long, Integer> cartMap, Long orderId, SkuDTO skuDTO) {
        OrderDetail detail = new OrderDetail();
        detail.setTitle(skuDTO.getTitle());
        detail.setSpec(skuDTO.getSpecialSpec());
        detail.setSkuId(skuDTO.getId());
        detail.setPrice(skuDTO.getPrice());
        detail.setOrderId(orderId);
        detail.setNum(cartMap.get(skuDTO.getId()));
        detail.setImage(StringUtils.substringBefore(skuDTO.getImages(), ","));
        return detail;
    }
}